package com.demo.sender.email;

import cn.hutool.core.lang.Validator;
import com.demo.annotation.EmailChannel;
import com.demo.common.ResultData;
import com.demo.enums.EmailChannelEnum;
import com.demo.request.EmailSenderReq;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ses.SesClient;
import software.amazon.awssdk.services.ses.model.*;

import javax.activation.DataHandler;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.*;
import javax.mail.util.ByteArrayDataSource;
import javax.naming.ServiceUnavailableException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * @author wangfengchen
 */
@EmailChannel(channel = EmailChannelEnum.AWS)
@Component
@Slf4j
public class AwsEmailSender implements EmailSenderService {

  /**
   * 最大重试次数
   */
  private static final int MAX_RETRIES = 3;
  /**
   * SES限制邮件大小不超过10MB
   */
  private static final int MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024;


  @Value("${email.aws.accessKey:not_set}")
  private String accessKey;
  @Value("${email.aws.secretKey:not_set}")
  private String secretKey;
  @Value("${email.aws.sender:not_set}")
  private String sender;
  @Value("${email.aws.region:not_set}")
  private String region;


  @Autowired
  private ThreadPoolTaskExecutor threadPoolTaskExecutor;

  private SesClient sesClient;


  @PostConstruct
  public void init() {
    sesClient = SesClient.builder()
        .region(Region.of(region))
        .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey)))
        .build();
    log.info("aws ses client初始化成功");
  }

  @PreDestroy
  public void destroy() {
    sesClient.close();
    log.info("aws ses client关闭成功");
  }


  /**
   * 发送邮件
   *
   * @param emailSenderReq
   * @return
   */
  @Override
  public ResultData<Object> send(EmailSenderReq emailSenderReq) {
    try {
      SendEmailRequest sendEmailRequest = createSendEmail(emailSenderReq.getToList(), emailSenderReq.getSubject(), emailSenderReq.getBodyHtml(), emailSenderReq.getBodyText());
      SendEmailResponse sendEmailResponse = null;
      int retryCount = 0;
      while (retryCount < MAX_RETRIES) {
        try {
          sendEmailResponse = sesClient.sendEmail(sendEmailRequest);
          log.info("sendEmail response = {}", sendEmailResponse);
          break;
        } catch (Exception e) {
          if (!(e.getCause() instanceof ServiceUnavailableException)) {
            throw e;
          }
          log.info("Retrying due to service unavailable", e);
          retryCount++;
        }
      }
      if (ObjectUtils.isEmpty(sendEmailResponse)) {
        return ResultData.fail();
      }
      return ResultData.success(sendEmailResponse.messageId());
    } catch (Exception e) {
      handleSendEmailException(e, emailSenderReq.getToList());
      return ResultData.fail();
    }
  }

  /**
   * 异步发送邮件
   *
   * @param emailSenderReq
   * @return
   */
  @Async
  @Override
  public ResultData<Object> sendAsync(EmailSenderReq emailSenderReq) {
    SendEmailRequest sendEmailRequest = createSendEmail(emailSenderReq.getToList(), emailSenderReq.getSubject(), emailSenderReq.getBodyHtml(), emailSenderReq.getBodyText());
    Future<String> future = threadPoolTaskExecutor.submit(() -> {
      try {
        SendEmailResponse sendEmailResponse = null;
        int retryCount = 0;
        while (retryCount < MAX_RETRIES) {
          try {
            sendEmailResponse = sesClient.sendEmail(sendEmailRequest);
            log.info("sendEmail response = {}", sendEmailResponse);
            break;
          } catch (SdkException e) {
            if (!(e.getCause() instanceof ServiceUnavailableException)) {
              throw e;
            }
            log.info("Retrying due to service unavailable", e);
            retryCount++;
          }
        }
        if (ObjectUtils.isEmpty(sendEmailResponse)) {
          return null;
        }
        return sendEmailResponse.messageId();
      } catch (Exception e) {
        handleSendEmailException(e, emailSenderReq.getToList());
        return null;
      }
    });
    if (ObjectUtils.isEmpty(future)) {
      return ResultData.fail();
    }
    String messageId = null;
    try {
      messageId = future.get();
    } catch (InterruptedException | ExecutionException e) {
      throw new RuntimeException(e);
    }
    return ResultData.success(messageId);
  }

  /**
   * 检查是否有剩余发送配额
   * 接近最大配额又不超过
   *
   * @return
   */
  public boolean checkSendQuota() {
    GetSendQuotaResponse quotaResponse = sesClient.getSendQuota();
    // 打印配额信息
    log.info("Max 24-hour send rate: " + quotaResponse.max24HourSend());
    log.info("Max send rate: " + quotaResponse.maxSendRate());
    log.info("Sent last hour: " + quotaResponse.sentLast24Hours());
    // 计算剩余配额（这里只计算了24小时内的剩余配额）
    double remainingQuota24Hours = quotaResponse.max24HourSend() - quotaResponse.sentLast24Hours();
    log.info("Remaining quota for 24 hours: " + remainingQuota24Hours);
    //发送量接近当前的配额不再使用，当前配置剩余3不再使用
    if (remainingQuota24Hours <= 3) {
      return false;
    }
    return true;
  }

  private SendEmailRequest createSendEmail(List<String> toList, String subject, String bodyHtml, String bodyText) {
    return SendEmailRequest.builder()
        .destination(Destination.builder().toAddresses(toList).build())
        .message(Message.builder()
            .body(Body.builder()
                .html(Content.builder().data(bodyHtml).build())
                .text(bodyText != null ? Content.builder().data(bodyText).build() : null)
                .build())
            .subject(Content.builder().data(subject).build())
            .build())
        .source(sender)
//        .configurationSetName(configurationSetName)
        .build();
  }

  /**
   * 处理发送邮件异常
   *
   * @param e      异常对象
   * @param toList
   */
  private void handleSendEmailException(Exception e, List<String> toList) {
    // 这里可以添加自定义的日志记录或异常处理逻辑
    log.info("handleSendEmailException error，toList = {}，error = {}", toList, ExceptionUtils.getStackTrace(e));
    // 其他业务处理
  }


  /**
   * 异步发送附件
   *
   * @param request
   */
  @Async
  @Retryable(value = {SesException.class}, maxAttempts = MAX_RETRIES, backoff = @Backoff(delay = 1000))
  @Override
  public void sendWithAttachmentAsync(EmailSenderReq request) {
    validateEmailRequest(request);
    MimeMessage message = buildMimeMessage(request);
    sendRawEmail(message);
  }

  private MimeMessage buildMimeMessage(EmailSenderReq request) {
    MimeMessage message = null;
    try {
      Session session = Session.getDefaultInstance(new Properties());
      message = new MimeMessage(session);
      message.setSubject(request.getSubject(), "UTF-8");
      message.setFrom(new InternetAddress(sender));
      message.setRecipients(MimeMessage.RecipientType.TO, InternetAddress.parse(String.join(",", request.getToList())));
      message.setSentDate(new Date());
      // 创建多部分内容
      MimeMultipart msgBody = new MimeMultipart("mixed");
      // 添加文本部分
      if (StringUtils.hasText(request.getBodyText())) {
        MimeBodyPart textPart = new MimeBodyPart();
        textPart.setContent(request.getBodyText(), "text/html; charset=UTF-8");
        msgBody.addBodyPart(textPart);
      }
      // 添加附件
      for (EmailAttachment attachment : request.getAttachmentList()) {
        validateAttachment(attachment);
        MimeBodyPart attPart = new MimeBodyPart();
        ByteArrayDataSource ds = new ByteArrayDataSource(
            attachment.getContent(),
            attachment.getContentType()
        );
        attPart.setDataHandler(new DataHandler(ds));
        attPart.setFileName(MimeUtility.encodeText(attachment.getFileName(), "UTF-8", "B"));
        msgBody.addBodyPart(attPart);
      }
      message.setContent(msgBody);
    } catch (MessagingException | UnsupportedEncodingException e) {
      log.error("buildMimeMessage 发生异常，exception = {}", ExceptionUtils.getStackTrace(e));
      throw new RuntimeException(e);
    }
    return message;
  }

  private void sendRawEmail(MimeMessage message) {
    try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
      message.writeTo(os);
      RawMessage rawMessage = RawMessage.builder()
          .data(SdkBytes.fromByteArray(os.toByteArray()))
          .build();
      SendRawEmailResponse response = sesClient.sendRawEmail(
          SendRawEmailRequest.builder()
              .rawMessage(rawMessage)
              .build());
      log.info("邮件发送成功，messageId = {}", response.messageId());
    } catch (IOException | MessagingException e) {
      log.error("邮件发送失败，exception = {}", ExceptionUtils.getStackTrace(e));
      throw new RuntimeException(e);
    }
  }

  private void validateEmailRequest(EmailSenderReq request) {
    if (!isValidEmail(request.getToList())) {
      log.error("无效的收件人邮箱地址，toList = {}", request.getToList());
      throw new IllegalArgumentException("无效的收件人邮箱地址");
    }
    long sum = request.getAttachmentList().stream().mapToLong(a -> a.getContent().length).sum();
    if (sum > MAX_ATTACHMENT_SIZE) {
      log.error("附件总大小超过10MB限制，sum = {}", sum);
      throw new IllegalArgumentException("附件总大小超过10MB限制");
    }
  }

  private void validateAttachment(EmailAttachment attachment) {
    int length = attachment.getContent().length;
    if (length > MAX_ATTACHMENT_SIZE) {
      log.error("附件大小超过10MB限制，fileName = {}，length = {}", attachment.getFileName(), length);
      throw new IllegalArgumentException(String.format("附件 %s 超过10MB限制", attachment.getFileName()));
    }
    if (!isSafeContentType(attachment.getContentType())) {
      log.error("禁止的文件类型，fileName = {}，contentType = {}", attachment.getFileName(), attachment.getContentType());
      throw new IllegalArgumentException(String.format("禁止的文件类型: %s", attachment.getContentType()));
    }
  }

  private boolean isValidEmail(List<String> emailList) {
    if (CollectionUtils.isEmpty(emailList)) {
      return false;
    }
    for (String email : emailList) {
      if (!Validator.isEmail(email)) {
        return false;
      }
    }
    return true;
  }

  private boolean isSafeContentType(String contentType) {
    // 实际应用中应根据安全策略扩展
    return !contentType.toLowerCase().contains("script");
  }


}

